#include "common.h"
#define __USE_GNU
#include "util-files.h"
#include "stru_queue.h"
#include "util_mmap.h"

/****
 * mmap 写文件接口
 */
void file_mmap(int fd, uint8 *tdata, uint64 dlen, size_t offset) {
    int flen = lseek(fd, dlen-1, SEEK_END);
    write(fd, "", 1);
    long int pageSize = dlen*offset;
    if(pageSize % 4096 != 0) {
        LOG_ERROR("#ERR: error to mmap file, pageSize not true, PageSize: %lld \n", pageSize);
        return;
    }
    uint8 *madr = (uint8 *)mmap(NULL, dlen, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, pageSize);
    memcpy(madr, tdata, dlen);
    int ret_unmmap = munmap(madr, dlen);
    if (ret_unmmap != 0) {
        LOG_DEBUG("关闭映射错误 \n");
        return;
    }
}
void test_oper_file_111(int fd, uint8 *data, long dlen, int times) {
    if (fd < 0) {
        LOG_ERROR("#ERR: error to open file, res: %d \n", fd);
        return;
    }
    size_t offset = 0;
    for(; offset < times; offset++) {
        // file_mmap(fd, data, dlen, offset);
        write(fd, data, dlen);
    }
    close(fd);
}

void test_oper_file(uint8 *data, long dlen, int times, int unit_index, int sum_index, int index) {
    char name[100];
    sprintf(&name, "/mnt/cephfs/data/test__%d_%d_%d.bin", unit_index, sum_index, index);
#ifdef O_DIRECT_TEST
    int fd = open(&name, O_RDWR | O_CREAT | O_DIRECT, 00777);
#else
    int fd = open(&name, O_RDWR | O_CREAT, 00777);
#endif
    test_oper_file_111(fd, data, dlen, times);
}

/***
 *  测试内存映射 
 */
float test_mmap1(long unit, long sum, size_t unit_index, size_t sum_index, int index) {
    long blen = unit;
    long times = sum / blen;
#ifdef O_DIRECT_TEST
    uint8 *rbuf = valloc(blen);
#elif
    uint8 *rbuf = malloc(blen);
#endif // DEBUG
    long i = 0;
    uint32 *pdt = (uint32 *) rbuf;
    for(i = 1; i <= blen/4; i++) {
        *pdt++ = i;
    }

    struct timeval tv;
    gettimeofday(&tv, NULL);
    int64_t start = (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
    test_oper_file(rbuf, blen, times, unit_index, sum_index, index);
    gettimeofday(&tv, NULL);
    int64_t end = (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
    float diff = (end - start)/1000.0;
    int gsum = sum/SIZE_GB;
    float lv = gsum*1.0/diff;
    LOG_DEBUG("index: %d, %d * %d: %lfs; lv: %lfGB/s \n", index, unit_index, sum_index, diff, lv);
    free(rbuf);
    return lv;
}

void test_mmap_cc() {
    uint64 t_sum[] = { 10*SIZE_GB, 80*SIZE_GB, 160*SIZE_GB };
    uint64 t_uint[] = { 10*SIZE_KB, 100*SIZE_KB, 1*SIZE_MB, 100*SIZE_MB, 1*SIZE_GB, 2*SIZE_GB};
    size_t i, j;
    for(i = 2; i < sizeof(t_sum); i++) {
        for (j = 0; j < sizeof(t_uint); j++)
        {
            size_t index = 0;
            float lv[4];
            float plv;
            for (; index < 4; index++)
            {
                lv[index] = test_mmap1(t_uint[j], t_sum[i], i, j, index);
            }
            plv = (lv[0] + lv[1] + lv[2] + lv[3])/4.0;
            printf("pingjun lv: %.02lf\n", plv);
        }
    }
    LOG_ERROR("测试写完毕 \n");
}
#include <pthread.h>
void *thread_mmap_test(MMapTest *mparm) {
    test_mmap1(mparm->unit, mparm->sum, mparm->thread_id, 0, 0);
}
void test_mmap_start() {
    int thread_num =  1;
    int plen = sizeof(MMapTest) * thread_num;
    MMapTest *mparm = malloc(plen);
    MMapTest *cmp = mparm;
    memset(mparm, 0x0, plen);
    size_t i;
    for(i = 0; i< thread_num; i++) {
        mparm->thread_id = i;
        mparm->unit = SIZE_GB * 1;
        mparm->sum = SIZE_GB * 50;
        pthread_create(&mparm->threadId, NULL, thread_mmap_test, (void *)mparm);
        *mparm++;
    }

    for(i = 0; i < thread_num; i++) {
        pthread_join(cmp->threadId, NULL);
        *cmp++;
    }
}
/**********裸IO测试多文件读写***********/
// #define TEST_1
#define FNUM 1
void * test_ODERIT_MFILE() {
    long tlen = SIZE_GB*1;
    long totle = SIZE_GB*1024;
    uint8 *tdata = valloc(tlen);
    int fds[FNUM] = { 0 };

    long i = 0;
    uint32 *pdt = (uint32 *) tdata;
    for(i = 1; i <= tlen/4; i++) {
        *pdt++ = i;
    }

    tdata[tlen - 1] = 0xAA;
    tdata[tlen - 2] = 0xAA;
    tdata[tlen - 3] = 0xAA;
    tdata[tlen - 4] = 0xAA;
    tdata[tlen - 5] = 0xAA;

    char fname[100]; 
    for (i = 0; i < FNUM; i++)
    {
        memset(&fname, 0x0, sizeof(fname));
        sprintf(&fname, "/mnt/cephfs/data/test_%d.bin", i);
        fds[i] = open(&fname, O_RDWR | O_CREAT | O_DIRECT, 00777);
        LOG_DEBUG("%02d ", fds[i]);
    }
    LOG_DEBUG("\n");

    struct timeval tv;
    gettimeofday(&tv, NULL);
    uint64 start = (uint64)tv.tv_sec * 1000 + tv.tv_usec / 1000;

#ifdef TEST_1
for (i = 0; i < 10; i++) //50 * 10.  500GB
{
    test_oper_file_111(fds[i], tdata, tlen, 50);
}
#endif

    i = 0;
    int fd;
    int count = 0;

#ifndef TEST_1
    while (1)
    {
        fd = fds[i % FNUM];
        write(fd, tdata, tlen);
        i++;
        if(i % FNUM == 0) {
            count++;
        }
        if(tlen*count >= totle) {
            break;
        }
    }
#endif
    gettimeofday(&tv, NULL);
    uint64 end = (uint64)tv.tv_sec * 1000 + tv.tv_usec / 1000;
    int diff = end - start;
    LOG_DEBUG("start: %d, end: %d, diff: %d, count: %d, tlen:%ld \n", start, end, diff, count, tlen);
    LOG_DEBUG("width: %.02lfGB/s\n", (count*tlen*FNUM*1.0)/(SIZE_GB*1.0)/(diff*1.0/1000.0));
}
void test_mmap() {
    // test_mmap_cc();
    // test_mmap_start();
    // test_ODERIT_MFILE();
    pthread_t threadId;
    pthread_create(&threadId, NULL, test_ODERIT_MFILE, NULL);
    pthread_join(threadId, NULL);
}
